/*
 * OpenParts
 * A dynamic-loading components framework for GWT
 * Copyright (C) 2011 Christophe Bouthier  [chris{AT}binary-gastronome{DOT}fr]
 *
 * This work is partially based on work I have done at INRIA (http://www.inria.fr) 
 * in the context of the Qualipso European Project (http://qualipso.org/),
 * The work done at INRIA is Copyright (C) 2006-2011 INRIA
 *
 * This work is distributed under the LGPL version 3
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation. See the GNU
 * Lesser General Public License in LGPL.txt for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package fr.openparts.OpenParts.container;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.MetaElement;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootLayoutPanel;

import fr.openparts.OpenParts.client.OPCommandCallback;
import fr.openparts.OpenParts.client.OPCommandFailureReason;
import fr.openparts.OpenParts.client.OPParams;
import fr.openparts.OpenParts.client.OPPartsLoader;
import fr.openparts.OpenParts.client.OPScheduler;
import fr.openparts.OpenParts.client.OPShell;

/**
 * Part container, taking care of part loading and displaying.
 * 
 * @author Christophe Bouthier [chris{AT}binary-gastronome{DOT}fr]
 * @creation.date 09 January 2010
 */
public class OPContainer implements EntryPoint {

    /**
     * Name of the meta-tag containing the name of the part to load.
     */
    public static final String PART_NAME_META_TAG = "part_name";

    /**
     * Name of the meta-tag containing the name of the resource to load.
     */
    public static final String PART_RESOURCE_META_TAG = "part_resource";

    /**
     * Name of the meta-tag containing the name of the view (action) to load.
     */
    public static final String PART_VIEW_META_TAG = "part_view";

    /**
     * Name of the meta-tags containing the name of the parameters to load and pass to the view.
     */
    public static final String PART_PARAMS_META_TAG = "part_params";

    /**
     * Entry point, method called when the module is loaded. Load the part described in the page with the meta tag named {@link #PART_NAME_META_TAG}.
     * 
     * @see com.google.gwt.core.client.EntryPoint#onModuleLoad()
     */
    @Override
    public void onModuleLoad() {
        OPScheduler.bind();
        OPPartsLoader.bind();
        loadPart();
    }

    /**
     * Search the information in the meta-tag and load the corresponding part. Display an error message if there is a problem loading or displaying the part.
     */
    protected void loadPart() {
        final String partName = getPartName();
        if (partName == null) {
            RootLayoutPanel.get().add(
                    new Label("The HTML page is missing a meta-tag with name " + PART_NAME_META_TAG + " and with value the name of the part to load."));
            return;
        }

        OPShell.displayViewAsRoot(partName, getPartResource(), getPartView(), getPartParams(), new OPCommandCallback() {
            @Override
            public void onSuccess() {}

            @Override
            public void onFail(final OPCommandFailureReason reason) {
                RootLayoutPanel.get().add(new Label(reason.getDescription()));
            }
        });
    }

    /**
     * Returns the name of the part to load, specified in the page through the {@link #PART_NAME_META_TAG} meta-tag. Returns null if there is no such meta-tag.
     * 
     * @return the name of the part to load. Null if there is no such meta-tag.
     */
    public String getPartName() {
        final String partName = getMetaTagContent(PART_NAME_META_TAG);
        assert partName != null : "part name shouldn't be null";
        return partName;
    }

    /**
     * Returns the part resource to load, specified in the page through the {@link #PART_RESOURCE_META_TAG} meta-tag. If there is no such meta-tag, use the part
     * name as the name of the resource.
     * 
     * @return the name of the part resource to load. Cannot be null.
     */
    public String getPartResource() {
        final String partResource = getMetaTagContent(PART_RESOURCE_META_TAG);
        return partResource == null ? getPartName() : partResource;
    }

    /**
     * Returns the part view to load, specified in the page through the {@link #PART_VIEW_META_TAG} meta-tag. If there is no such meta-tag, use
     * {@link OPShell#ACTION_ROOT} as the name of the view.
     * 
     * @return the name of the part view to load. Cannot be null.
     */
    public String getPartView() {
        final String partView = getMetaTagContent(PART_VIEW_META_TAG);
        return partView == null ? OPShell.ACTION_ROOT : partView;
    }

    /**
     * Returns the params to pass to the loaded view, specified in the page through the {@link #PART_PARAMS_META_TAG} meta-tag. Return null if there is no such
     * meta-tag.
     * 
     * The meta-tag content should be a list of an even number of strings, separated by commas. Each odd item is a key, each even item is the value for this
     * key.
     * 
     * @return the params to pass to the view. Can be null.
     */
    public OPParams getPartParams() {
        final String partParamsRaw = getMetaTagContent(PART_PARAMS_META_TAG);
        if (partParamsRaw == null) {
            return null;
        }

        // split and trim the parameters
        final String[] params = partParamsRaw.split(",");
        for (int i = 0; i < params.length; i++) {
            params[i] = params[i].trim();
        }

        return new OPParams(params);
    }

    /**
     * Returns the content of the meta tag with name given. Only return the first found, if there's more than one. Returns null if there is no such meta tag.
     * 
     * @param metaTagName
     *            the name of the meta-tag to search for
     * @return the content found in the first meta-tag given, or null if there is no such meta-tag.
     */
    protected String getMetaTagContent(final String metaTagName) {
        assert metaTagName != null : "parameter metaTagName shouldn't be null";

        String content = null;

        final NodeList<Element> metas = Document.get().getElementsByTagName("meta");
        for (int i = 0; i < metas.getLength(); i++) {
            Element el = metas.getItem(i);
            MetaElement meta = el.cast();
            if (metaTagName.equals(meta.getName())) {
                content = meta.getContent();
                break;
            }
        }

        return content;
    }

}
